/**
****************************************************************
* @file felica.c
*
* @brief  关于felica协议一些基本的底层驱动接口
*
* @author
*
*
****************************************************************
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include "felica.h"
#include "nfc_hal_config.h"
#include "nfc_hal_transfer.h"
#include "bit_define.h"

extern uint8_t NFC_DEBUG_LOG;

int pcd_request_c(uint8_t *pSysCode,
                  uint8_t bReqCode,
                  uint8_t bSlot,
                  uint8_t bRate,
                  uint8_t *pResponse)
{
    uint32_t  wTimeOutUs;
    int status = HAL_STATUS_OK;
    hal_nfc_transceive_t  pi;

    if(NFC_DEBUG_LOG)
        printf("REQC:\n");
    switch (bSlot)
    {
    case PHPAL_FELICA_NUMSLOTS_1:
    case PHPAL_FELICA_NUMSLOTS_2:
    case PHPAL_FELICA_NUMSLOTS_4:
    case PHPAL_FELICA_NUMSLOTS_8:
    case PHPAL_FELICA_NUMSLOTS_16:
        wTimeOutUs = PHPAL_FELICA_SW_RESP_TIME_A_US + ((bSlot + 1) * PHPAL_FELICA_SW_RESP_TIME_B_US);
        if(hal_nfc_read_register(REG_TXMODE) & PHHAL_HW_BIT_212KBPS)
        {
            wTimeOutUs -= 226;
        }
        else
        {
            wTimeOutUs -= 113;
        }
        break;
    default:
        if(NFC_DEBUG_LOG)
            printf("SLOT_ERROR = %02x\n", bSlot);
        return HAL_STATUS_USER_ERR;//PH_ADD_COMPCODE(PH_ERR_INVALID_PARAMETER, PH_COMP_PAL_FELICA);
    }
    pcd_set_reqc_tmo(wTimeOutUs, bRate);

    if(bSlot > PHPAL_FELICA_NUMSLOTS_1)		//配置多重接收
    {
        hal_nfc_write_register(REG_RXMODE, (hal_nfc_read_register(REG_RXMODE) | PHHAL_HW_BIT_RXMULTIPLE));
    }

    pi.type = HAL_TECH_TYPE_F;
    pi.mf_command = PCD_TRANSCEIVE;
    pi.mf_length = 6;
    pi.mf_data[0] = pi.mf_length;
    pi.mf_data[1] = PHPAL_FELICA_SW_CMD_REQC;
    pi.mf_data[2]	= pSysCode[0];
    pi.mf_data[3] = pSysCode[1];
    pi.mf_data[4] = bReqCode;
    pi.mf_data[5] = bSlot;
    status = pcd_com_transceive(&pi);

    if(bSlot > PHPAL_FELICA_NUMSLOTS_1)
    {
        hal_nfc_write_register(REG_RXMODE, (hal_nfc_read_register(REG_RXMODE) & (~PHHAL_HW_BIT_RXMULTIPLE)));
    }
    if (status != HAL_STATUS_OK)
    {
        return status;
    }
    if((pi.mf_data[0] != pi.mf_length / 8)
            || (pi.mf_data[0] != PHPAL_FELICA_ATQC_LENGTH)
            || (pi.mf_data[1] != PHPAL_FELICA_SW_RSP_REQC))
    {
        return PROTOCOL_ERR;
    }
    memcpy(pResponse, pi.mf_data, PHPAL_FELICA_ATQC_LENGTH);

    return HAL_STATUS_OK;
}

int pcd_req_resp(uint8_t *pIdmPMm, uint8_t *pMode)
{
    int  status;
    hal_nfc_transceive_t  pi;

    if(NFC_DEBUG_LOG)
        printf("REQUEST RESPONSE:\n");
    pcd_set_felica_tmo(0, pIdmPMm[11]);
    pi.type = HAL_TECH_TYPE_F;
    pi.mf_command = PCD_TRANSCEIVE;
    pi.mf_length = 10;
    pi.mf_data[0] = pi.mf_length;
    pi.mf_data[1] = PHPAL_FELICA_SW_CMD_REQ_RESP;
    memcpy(&pi.mf_data[2], pIdmPMm, PHPAL_FELICA_IDM_LENGTH);

    status = pcd_com_transceive(&pi);

    if (status != HAL_STATUS_OK)
    {
        return status;
    }

    /* Check length */
    if((pi.mf_data[0] != pi.mf_length / 8)
            || (pi.mf_data[0] != FELICA_REQUEST_RESPONSE_RSP_LEN + 1)
            || (pi.mf_data[1] != FELICA_REQUEST_RESPONSE_RES))
    {
        return PROTOCOL_ERR;
    }

    *pMode = pi.mf_data[FELICA_REQUEST_RESPONSE_RSP_LEN];
    return HAL_STATUS_OK;
}

int pcd_req_service(uint8_t *pIdmPMm,
                    uint8_t  bTxNumServices,
                    uint8_t *pTxServiceList,
                    uint8_t *pRxNumServices,
                    uint8_t *pRxServiceList)
{
    int  status = HAL_STATUS_OK;
    hal_nfc_transceive_t  pi;
    if(NFC_DEBUG_LOG)
        printf("REQUEST SERVICE:\n");
    pcd_set_felica_tmo(bTxNumServices, pIdmPMm[10]);

    pi.mf_command = PCD_TRANSCEIVE;
    pi.type = HAL_TECH_TYPE_F;
    pi.mf_length = 11 + (bTxNumServices * 2);
    pi.mf_data[0] = pi.mf_length;
    pi.mf_data[1] = PHPAL_FELICA_SW_CMD_REQ_SERVICE;
    memcpy(&pi.mf_data[2], pIdmPMm, PHPAL_FELICA_IDM_LENGTH);
    pi.mf_data[10] = bTxNumServices;
    memcpy(&pi.mf_data[11], pTxServiceList, (bTxNumServices * 2));

    status = pcd_com_transceive(&pi);

    if (status != HAL_STATUS_OK)
    {
        return status;
    }

    /* Check length */
    if((pi.mf_data[0] != pi.mf_length / 8)
            || (pi.mf_data[0] != (11 + (bTxNumServices * 2)))
            || (pi.mf_data[1] != FELICA_REQUEST_SERVIVE_RES))
    {
        return HAL_STATUS_PROTOCOL_ERR;
    }
    *pRxNumServices = pi.mf_data[10];
    memcpy(pRxServiceList, &pi.mf_data[11], *pRxNumServices * 2);

    return HAL_STATUS_OK;
}

int pcd_read_without_encryption(uint8_t *pIdmPMm,
                                uint8_t  bNumServices,
                                uint8_t *pServiceList,
                                uint8_t  bTxNumBlocks,
                                uint8_t *pBlockList,
                                uint8_t  bBlockListLength,
                                uint8_t *pRxNumBlocks,
                                uint8_t *pBlockData,
                                uint8_t *pStatusFlag)
{
    int  status = HAL_STATUS_OK;
    hal_nfc_transceive_t  pi;

    if(NFC_DEBUG_LOG)
        printf("FELICA READ:\n");
    pcd_set_felica_tmo(bNumServices, pIdmPMm[13]);

    pi.type = HAL_TECH_TYPE_F;
    pi.mf_command = PCD_TRANSCEIVE;
    pi.mf_length = 12 + (2 * bNumServices) + (bBlockListLength);		//(2 * bTxNumBlocks) <= bBlockListLength <= (3 * bTxNumBlocks)
    pi.mf_data[0] = pi.mf_length;
    pi.mf_data[1] = PHPAL_FELICA_SW_CMD_READ;
    memcpy(&pi.mf_data[2], pIdmPMm, PHPAL_FELICA_IDM_LENGTH);
    pi.mf_data[10] = bNumServices;	//number of service
    memcpy(&pi.mf_data[11], pServiceList, (2 * bNumServices));
    pi.mf_data[11 + (2 * bNumServices)] = bTxNumBlocks;	//number of block
    memcpy(&pi.mf_data[11 + (2 * bNumServices) + 1], pBlockList, bBlockListLength);

    status = pcd_com_transceive(&pi);

    if (status != HAL_STATUS_OK)
    {
        return status;
    }

    /* Check length */
    if((pi.mf_data[0] != pi.mf_length / 8)
            || (pi.mf_data[0] != (13 + (pi.mf_data[12] * 16)))
            || (pi.mf_data[1] != FELICA_READ_WITHOUT_ENCRYPTION_RES))
    {
        return HAL_STATUS_PROTOCOL_ERR;
    }

    *pRxNumBlocks = pi.mf_data[12];
    memcpy(pStatusFlag, &pi.mf_data[10], 2);
    memcpy(pBlockData, &pi.mf_data[13], (pi.mf_data[12] * 16));

    return HAL_STATUS_OK;
}

int pcd_write_without_encryption(uint8_t *pIdmPMm,
                                 uint8_t  bNumServices,
                                 uint8_t *pServiceList,
                                 uint8_t  bNumBlock,
                                 uint8_t *pBlockList,
                                 uint8_t  bBlockListLength,
                                 uint8_t *pBlockData,
                                 uint8_t *pStatusFlag)
{
    int  status = HAL_STATUS_OK;
    hal_nfc_transceive_t  pi;
    if(NFC_DEBUG_LOG)
        printf("FELICA WRITE:\n");
    pcd_set_felica_tmo(bNumServices, pIdmPMm[14]);
    pi.type = HAL_TECH_TYPE_F;
    pi.mf_command = PCD_TRANSCEIVE;
    pi.mf_length = 12 + (2 * bNumServices) + (bBlockListLength) + ((16 * bNumBlock));
    pi.mf_data[0] = pi.mf_length;
    pi.mf_data[1] = PHPAL_FELICA_SW_CMD_WRITE;
    memcpy(&pi.mf_data[2], pIdmPMm, PHPAL_FELICA_IDM_LENGTH);
    pi.mf_data[10] = bNumServices;	//number of service
    memcpy(&pi.mf_data[11], pServiceList, (2 * bNumServices));
    pi.mf_data[11 + (2 * bNumServices)] = bNumBlock;	//number of block
    memcpy(&pi.mf_data[11 + (2 * bNumServices) + 1], pBlockList, bBlockListLength);
    memcpy(&pi.mf_data[11 + (2 * bNumServices) + 2], pBlockData, (16 * bNumBlock));

    status = pcd_com_transceive(&pi);

    if (status != HAL_STATUS_OK)
    {
        return status;
    }

    /* Check length */
    if((pi.mf_data[0] != pi.mf_length / 8)
            || (pi.mf_data[0] != (FELICA_WRITE_WITHOUT_ENCRYPTION_RSP_LEN))
            || (pi.mf_data[1] != FELICA_WRITE_WITHOUT_ENCRYPTION_RES))
    {
        return HAL_STATUS_PROTOCOL_ERR;
    }

    memcpy(pStatusFlag, &pi.mf_data[10], 2);

    return HAL_STATUS_OK;
}

int pcd_req_syscode(uint8_t *pIdmPMm,  uint8_t *pSysCodeNum, uint8_t *pSysCodeList)
{
    int  status = HAL_STATUS_OK;
    hal_nfc_transceive_t  pi;

    if(NFC_DEBUG_LOG)
        printf("REQUSET SYSTEM CODE:\n");
    pcd_set_felica_tmo(0, pIdmPMm[11]);
    pi.type = HAL_TECH_TYPE_F;
    pi.mf_command = PCD_TRANSCEIVE;
    pi.mf_length = 10;
    pi.mf_data[0] = pi.mf_length;	//LEN
    pi.mf_data[1] = PHPAL_FELICA_SW_CMD_REQ_SYS_CODE;
    memcpy(&pi.mf_data[2], pIdmPMm, PHPAL_FELICA_IDM_LENGTH);

    status = pcd_com_transceive(&pi);

    if (status != HAL_STATUS_OK)
    {
        return status;
    }

    /* Check length */
    if((pi.mf_data[0] != pi.mf_length / 8)
            || (pi.mf_data[0] != (11 + (2 * pi.mf_data[10])))
            || (pi.mf_data[1] != FELICA_REQUEST_SYS_CODE_RES))
    {
        return HAL_STATUS_PROTOCOL_ERR;
    }

    *pSysCodeNum = pi.mf_data[10];
    memcpy(pSysCodeList, &pi.mf_data[11], (2 * (*pSysCodeNum)));

    return HAL_STATUS_OK;

}

void pcd_set_reqc_tmo(uint16_t wTimeout, uint16_t wDataRate)
{
    float     fTimeoutUs;
    float     fTemp;
    uint8_t       bRegister;
    uint16_t      wPrescaler;
    uint16_t      wReload;

    /* Retrieve RRate */
    wDataRate++;

    /* Handle timeout in microseconds */
    fTimeoutUs = (float)wTimeout;

    /* Add timershift to timeout value */
    fTimeoutUs = fTimeoutUs + (PHHAL_HW_TIMER_SHIFT * (PHHAL_HW_ETU_106 / (float)wDataRate));

    /* Add digital delay for Felica */
    fTimeoutUs = fTimeoutUs + PHHAL_HW_FELICA_ADD_DELAY_US;

    /* Max. timeout check */
    if (fTimeoutUs > PHHAL_HW_TIMER_MAX_VALUE_US)
    {
        return;//PH_ADD_COMPCODE(PH_ERR_PARAMETER_OVERFLOW, PH_COMP_HAL);
    }

    /* Calculate the prescaler value */
    /* [timeout[us]  = (1 + 2n) / 13.56] */
    fTemp = (((fTimeoutUs * PHHAL_HW_TIMER_FREQ) / 65535.0f) - 1.0f) / 2.0f;

    /* Except for a special case, the float32_t value will have some commas
    and therefore needs to be "ceiled" */
    if (fTemp > (float)(uint16_t)fTemp)
    {
        wPrescaler = (uint16_t)(fTemp + 1);
    }
    else
    {
        wPrescaler = (uint16_t)fTemp;
    }

    /* Calculate the reload value */
    fTemp = (fTimeoutUs * PHHAL_HW_TIMER_FREQ) / (1.0f + (2.0f * (float)wPrescaler));

    /* Except for a special case, the float32_t value will have some commas
    and therefore needs to be "ceiled" */
    if (fTemp > (float)(uint16_t)fTemp)
    {
        wReload = (uint16_t)(fTemp + 1);
    }
    else
    {
        wReload = (uint16_t)fTemp;
    }

    /* write Prescaler value */
    hal_nfc_write_register(REG_TPRESCALER, (uint8_t)(wPrescaler & 0xFF));
    bRegister = hal_nfc_read_register(REG_TMODE);
    bRegister &= (uint8_t)~(uint8_t)PHHAL_HW_MASK_TPRESCALER_HI;
    hal_nfc_write_register(REG_TMODE, 0x80 | bRegister | (uint8_t)((wPrescaler >> 8) & 0x0F));

    /* write Counter value */
    hal_nfc_write_register(REG_TRELOADL, (uint8_t)(wReload & 0xFF));
    hal_nfc_write_register(REG_TRELOADH, (uint8_t)((wReload >> 8) & 0xFF));
}

void pcd_set_felica_tmo(uint8_t n, uint8_t Position)
{
    uint8_t  A, B, E;
    uint16_t  tmp;

    A = (Position & (BIT(0) | BIT(1) | BIT(2)));
    B = (Position & (BIT(3) | BIT(4) | BIT(5))) >> 3;
    E = (Position & (BIT(6) | BIT(7))) >> 6;
//	printf("a = %02x;b = %02x;e = %02x\n",(u16)A,(u16)B,(u16)E);
    tmp = ((B + 1) * n + (A + 1)) * pow(4, E);

    hal_nfc_write_register(REG_TPRESCALER, (FELICA_FDT_302us/* + TP_dFWT*/) & 0xFF);
    hal_nfc_write_register(REG_TMODE, BIT(7) | (((FELICA_FDT_302us/* + TP_dFWT*/)>>8) & 0xFF));
//	printf("FWT = %u us\n", (u16)(tmp * 302));
    hal_nfc_write_register(REG_TRELOADL, tmp);//  302 * (Reload + 1)
    hal_nfc_write_register(REG_TRELOADH, (tmp >> 8));
}
